package com.tianji.remark.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.dto.remark.LikedTimesDTO;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author hc
 * @since 2024-08-26
 */
@Slf4j
@Service
public class LikedRecordPlusServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private final static String REMARK_RECORD_KEY = "remark:record:set:liked:bizid:";

    private final static String REMARK_RECORD_LIKED_TIMED_KEY = "remark:record:zset:likedTimes:";

    /**
     * 添加或取消点赞
     *
     * @param likeRecordFormDTO 点赞数据
     */
    @Override
    public void addOrCancelLiked(LikeRecordFormDTO likeRecordFormDTO) {
        //1、判断传参是否为空
        if (!ObjectUtil.isAllNotEmpty(likeRecordFormDTO)) {
            log.error("点赞数据传参异常，数据为：{}", likeRecordFormDTO);
            return;
        }

        //2、判断是否是点赞
        boolean isSuccess = likeRecordFormDTO.getLiked() ? liked(likeRecordFormDTO) : unliked(likeRecordFormDTO);

        //3、统计点赞数
        if (!isSuccess) {
            log.error("新增或者删除失败，数据为：{}", likeRecordFormDTO);
            return;
        }
        Long size = redisTemplate.opsForSet().size(REMARK_RECORD_KEY + likeRecordFormDTO.getBizId());

        //4、将点赞总数缓存到redis中
        redisTemplate.opsForZSet().add(
                REMARK_RECORD_LIKED_TIMED_KEY + likeRecordFormDTO.getBizType(),
                likeRecordFormDTO.getBizId().toString(),
                size == null ? 0 : size);
    }

    /**
     * 查询当前用户是否点赞了指定的业务
     *
     * @param bizIds 业务id
     * @return 当前用户点赞过的业务id
     */
    @Override
    public List<Long> queryIsLikedBizIds(List<Long> bizIds) {
        //1、判断传参数据是否为空
        if (ObjectUtil.isEmpty(bizIds)) {
            log.error("点赞bizId传参异常，数据为：{}", bizIds);
            return Collections.EMPTY_LIST;
        }

        //2、查询当前用户是否点赞了指定的业务
        List<Object> objects = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection stringConnection = (StringRedisConnection) connection;
            for (Long bizId : bizIds) {
                stringConnection.sIsMember(REMARK_RECORD_KEY + bizId, UserContext.getUser().toString());
            }
            return null;
        });

        //3、返回结果

        return IntStream
                .range(0, objects.size())
                .filter(b -> (Boolean) objects.get(b))
                .mapToObj(bizIds::get)
                .collect(Collectors.toList());
    }

    /**
     * 将点赞次数通知到业务方
     *
     * @param bizType          业务类型
     * @param maxBizSize 最大处理任务数
     */
    @Override
    public void readLikedTimesAndSendMessage(String bizType, int maxBizSize) {
        //1、从redis中循环读取并移除bizId和点赞总数
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(REMARK_RECORD_LIKED_TIMED_KEY + bizType, maxBizSize);
        if(typedTuples==null){
            return;
        }
        while (typedTuples.size()>0){
            //1.1、组装消息体内容
            List<LikedTimesDTO> msgList = typedTuples.stream()
                    .map(t -> new LikedTimesDTO(Long.valueOf(t.getValue()), t.getScore().intValue()))
                    .collect(Collectors.toList());
            //2、通过mq发消息通知业务方
            rabbitTemplate.convertAndSend(
                    LIKE_RECORD_EXCHANGE,
                    StringUtils.format(LIKED_TIMES_KEY_TEMPLATE,bizType),
                    msgList);
            typedTuples = redisTemplate.opsForZSet().popMin(REMARK_RECORD_LIKED_TIMED_KEY + bizType, maxBizSize);
        }
    }

    //点赞业务
    private boolean liked(LikeRecordFormDTO likeRecordFormDTO) {
        //1、从redis中读取是否有记录
        Set<String> members = redisTemplate.opsForSet().members(REMARK_RECORD_KEY + likeRecordFormDTO.getBizId());


        if (ObjectUtil.isNotEmpty(members) && members.contains(UserContext.getUser().toString())) {
            log.error("点赞记录已存在,点赞记录为：{}", members);
            return false;
        }

        //2、未点过赞，在redis中新增点赞记录
        Long add = redisTemplate.opsForSet().add(REMARK_RECORD_KEY + likeRecordFormDTO.getBizId(), UserContext.getUser().toString());
        if (add == null) {
            return false;
        }
        return add > 0;
    }

    //取消点赞业务
    private boolean unliked(LikeRecordFormDTO likeRecordFormDTO) {
        //1、直接删除
        Long remove = redisTemplate.opsForSet().remove(REMARK_RECORD_KEY + likeRecordFormDTO.getBizId(), UserContext.getUser().toString());
        if (remove == null) {
            return false;
        }

        return remove > 0;
    }
}
